#include "run.hh"

static void init(t_toolchain& toolchain)
{
  for (t_input_vars_map::iterator it =
	 toolchain.input_variables.begin();
       it != toolchain.input_variables.end();
       ++it)
    it->second = cvCaptureFromAVI( it->first.c_str() );

  double fps;
  CvSize size;
  { // All ouput vids have the same parameter as the firt input
    CvCapture* first_vid = ( toolchain.input_variables.begin() )->second;

    fps = cvGetCaptureProperty (first_vid, CV_CAP_PROP_FPS);
    
    size = cvSize((int)cvGetCaptureProperty( first_vid, CV_CAP_PROP_FRAME_WIDTH),
		  (int)cvGetCaptureProperty( first_vid, CV_CAP_PROP_FRAME_HEIGHT));
  }

  for (t_output_vars_map::iterator it =
	 toolchain.output_variables.begin();
       it != toolchain.output_variables.end();
       ++it)
    { 
      it->second = cvCreateVideoWriter (it->first.c_str(),
					CV_FOURCC('D','I','V','X'),
					fps,
					size);
    }
}

static void close(t_toolchain& toolchain)
{
  for (t_input_vars_map::iterator it =
	 toolchain.input_variables.begin();
       it != toolchain.input_variables.end();
       ++it)
    cvReleaseCapture( &(it->second) );

  for (t_output_vars_map::iterator it =
	 toolchain.output_variables.begin();
       it != toolchain.output_variables.end();
       ++it)
    cvReleaseVideoWriter( &(it->second) );
}

static bool run_stage_once(const t_stage& stage,
			   t_variables_map& variables)
{
  if (stage.operation.compare("invert") == 0)
    {
      variables[stage.ret] = invert( variables[stage.args.front()] );
    }
  else if (stage.operation.compare("threshold") == 0)
    {
      variables[stage.ret] = threshold( variables[stage.args.front()],
					atoi(stage.args[1].c_str()));
    }
  else if (stage.operation.compare("convolution") == 0)
    {
      variables[stage.ret] = convolution( variables[stage.args.front()],
					  atoi(stage.args[1].c_str()),
					  atoi(stage.args[2].c_str()),
					  atoi(stage.args[3].c_str()),
					  atoi(stage.args[4].c_str()),
					  atoi(stage.args[5].c_str()),
					  atoi(stage.args[6].c_str()),
					  atoi(stage.args[7].c_str()),
					  atoi(stage.args[8].c_str()),
					  atoi(stage.args[9].c_str()));
    }
  else if (stage.operation.compare("incrustation") == 0)
    {
      variables[stage.ret] = incrustation( variables[stage.args[0]],
					   variables[stage.args[1]],
					   atoi(stage.args[2].c_str()),
					   atoi(stage.args[3].c_str()),
					   atoi(stage.args[4].c_str()),
					   atoi(stage.args[5].c_str()));
    }
  else if (stage.operation.compare("sobel") == 0)
    {
      variables[stage.ret] = sobel( variables[stage.args.front()],
				    atoi(stage.args[1].c_str()),
				    atoi(stage.args[2].c_str()));
    }
  else if (stage.operation.compare("mix") == 0)
    {
      variables[stage.ret] = mix( variables[stage.args.front()],
				  variables[stage.args[1]],
				  stage.args[2].c_str());
    }

  return false;
}

static bool run_toolchain_once(t_toolchain& toolchain)
{
  for (std::list<t_stage>::const_iterator it = toolchain.stages.begin();
       it != toolchain.stages.end(); ++it)
    {
      run_stage_once(*it, toolchain.variables);
    }

  return false;
}

void run(t_toolchain& toolchain)
{
  std::cout << std::endl
	    << "***** Running following toolchain *****"
	    << std::endl
	    << std::endl
	    << toolchain.to_string()
	    << std::endl
	    << "***************************************"
	    << std::endl;

  init(toolchain);

  bool no_more_frames = false;

  // Run the toolchain on each frame
  while ( !no_more_frames )
    {
      { // Init variables with next frame of input variables
	no_more_frames = true;

	for (t_input_vars_map::iterator it =
	       toolchain.input_variables.begin();
	     it != toolchain.input_variables.end();
	     ++it)
	  {
	    IplImage* frame = cvQueryFrame( it->second );
	    toolchain.variables[it->first] = frame;
	    if (frame != NULL)
	      no_more_frames = false;
	  }

	if (!no_more_frames)
	  { // run the toolchain
	    no_more_frames = run_toolchain_once(toolchain);

	    for (t_output_vars_map::iterator it =
		   toolchain.output_variables.begin();
		 it != toolchain.output_variables.end();
		 ++it)
	      {
		cvWriteFrame( it->second, toolchain.variables[it->first]);
	      }
	  }
      }
    }

  close(toolchain);
}
